package com.rainyun.rc.orm;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.rainyun.rc.common.VersionUtil;
import com.rainyun.rc.orm.annotation.FieldInfo;
import com.rainyun.rc.orm.annotation.IgnoreField;
import com.rainyun.rc.orm.annotation.PrimaryField;
import com.rainyun.rc.orm.annotation.ReferData;
import com.rainyun.rc.orm.annotation.ReferField;
import com.rainyun.rc.config.ErrorHandler;
import com.rainyun.rc.common.VersionException;

public abstract class CommonDB implements RainDB{

	/**记录类的信息*/
	protected Map<Class<?>,ClazzInfo> clazzInfos = new HashMap<Class<?>, ClazzInfo>();
	/**记录类的被关联信息*/
	protected Map<Class<?>,List<ReferInfo>> referedInfos = new HashMap<>();
	/**各类的路径(用路径为键储存类的信息)*/
	protected Map<String,ClazzInfo> paths = new LinkedHashMap<>();
	
	/**储存上一个语句 的错误*/
	protected Exception recentError;
	protected boolean debug;

	/**连接*/
	protected Connection connection;
	protected ErrorHandler err_handler;
	
	@Override
	public RainDB addClazz(Class<?> clazz, String path) {
		ClazzInfo info = new ClazzInfo(clazz, path);
		Map<String,Field> fm = new HashMap<String, Field>();
		List<String> refs = new ArrayList<String>();
		List<String> refds = new ArrayList<String>();
		for(Field f:clazz.getDeclaredFields()) {
			if(f.isAnnotationPresent(IgnoreField.class))continue;
			String name = f.getName();
			FieldInfo finfo = f.getAnnotation(FieldInfo.class);
			if(finfo!=null) {
				name = finfo.col_name().isEmpty()?name:finfo.col_name();
			}
			if(f.isAnnotationPresent(PrimaryField.class))info.setPrimary(name);
			if(f.isAnnotationPresent(ReferField.class)) {
				refs.add(name);
				ReferField rfinfo = f.getAnnotation(ReferField.class);
				ReferInfo referinfo = new ReferInfo(clazz,name,rfinfo);
				List<ReferInfo> list = referedInfos.get(rfinfo.to());
				if(list==null) {
					list = new ArrayList<ReferInfo>();
					referedInfos.put(rfinfo.to(), list);
				}
				list.add(referinfo);
			}
			if(f.isAnnotationPresent(ReferData.class))refds.add(name);
			f.setAccessible(true);
			fm.put(name,f);
		}
		
		info.setFields(fm);
		info.setReferKeys(refs);
		info.setReferDataFields(refds);
		clazzInfos.put(clazz,info);
		paths.put(path,info);
		return this;
	}
	
	@Override
	public ClazzInfo getClazzInfo(Class<?> clazz) {
		return clazzInfos.get(clazz);
	}
	/**获取类的被关联信息*/
	public List<ReferInfo> getReferedInfo(Class<?> clazz) {
		return referedInfos.get(clazz);
	}
	@Override
	public ClazzInfo getClazzInfo(String path) {
		return paths.get(path);
	}
	

	public List<Map<String, Object>> getResultFromSet(ResultSet re){
		List<Map<String, Object>> lmap = new ArrayList<Map<String,Object>>();
		try {
			while (re.next()) {
				Map<String, Object> map = new LinkedHashMap<String,Object>();
				ResultSetMetaData meta = re.getMetaData();
				for (int i = 1; i <= meta.getColumnCount(); i++) {
					String cname = meta.getColumnLabel(i);
					map.put(cname,re.getObject(i));
				}
				lmap.add(map);
			}
			return lmap;
		} catch (SQLException e) {
			handleError(null,e);
		}
		return null;
	}
	
	public void handleError(String path,Exception e) {
		recentError = e;
		if(err_handler!=null) {
			err_handler.handleError(path, e);
		}else e.printStackTrace();
	}
	@Override
	public void setErrorHandler(ErrorHandler handler) {
		this.err_handler = handler;
	}
	
	@Override
	public Exception getError() {
		return recentError;
	}
	
	@Override
	public boolean initAll() throws VersionException, SQLException {
		for(Class<?> c:clazzInfos.keySet()) {
			if(!newSql(c).init())return false;
		}
		return true;
	}
	
	@Override
	public Map<Class<?>, ClazzInfo> getAllClazzesInfo() {
		return clazzInfos;
	}

	public List<Map<String, Object>> sqlQuery(String sql,Object... arg) throws SQLException{
		PreparedStatement sta = null;
		Connection con = connection;
		sql = sql.replace(";","");//反注入
		if(debug)System.out.println(sql);
		sta = con.prepareStatement(sql);
		appStaArg(sta, arg);
		
		ResultSet re = null;
		try {
			re = sta.executeQuery();
			List<Map<String, Object>> result = getResultFromSet(re);
			recentError = null;
			return result;
		} catch (SQLException e) {
			throw e;
		}finally {
			try {
				if(sta!=null&&!sta.isClosed())sta.close();
				if(re!=null&&!re.isClosed())re.close();
			} catch (SQLException e) {
				handleError(null,e);
			}
		}
	}
	
	public int sqlUpdate(String sql,Object... arg) throws SQLException {
		PreparedStatement sta = null;
		try {
			Connection con = connection;
			sql = sql.replace(";","");//反注入
			if(debug)System.out.println(sql);
			sta = con.prepareStatement(sql);
			appStaArg(sta, arg);
			int re = sta.executeUpdate();
			recentError = null;
			return re;
		} catch (SQLException e) {
			throw e;
		}finally {
			if(sta!=null)
			try {
				sta.close();
			} catch (SQLException e1) {
				throw e1;
			}
		}
	}
	
	public void closeResult(ResultSet re) {
		try {
			re.getStatement().close();
		} catch (SQLException e) {
			handleError(null,e);
		}
	}
	
	private void appStaArg(PreparedStatement sta,Object[] arg) throws SQLException {
		for (int i = 0; i < arg.length; i++) {
			sta.setObject(i+1,arg[i]);
		}
	}
	
	public void closeConnect() {
		try {
			connection.close();
		} catch (SQLException e) {
			handleError(null,e);
		}
	}

	/**创建连接
	 * @throws SQLException */
	public abstract boolean connect() throws SQLException;
	
	@Override
	public boolean isDebug() {
		return debug;
	}
	@Override
	public void setDebug(boolean debug) {
		this.debug = debug;
	}
	
	@Override
	public boolean begin() throws SQLException {
		try {
			connection.setAutoCommit(false);
			return true;
		} catch (SQLException e) {
			handleError(null,e);
			throw e;
		} 
	}
	@Override
	public boolean rollback() throws SQLException {
		try {
			connection.rollback();
			connection.setAutoCommit(true);
			return true;
		} catch (SQLException e) {
			handleError(null,e);
			throw e;
		}
	}
	@Override
	public boolean commit() throws SQLException {
		try {
			connection.commit();
			connection.setAutoCommit(true);
			return true;
		} catch (SQLException e) {
			handleError(null,e);
			throw e;
		}
	}
	/**设置当前数据表的版本为当前实体类版本*/
	public abstract void setDataVersion(Class<?> c);
	/**获取当前数据表的的版本*/
	public abstract String getDataVersion(Class<?> c);

}




